﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.ComponentModel.Design;

using EnvDTE;

using EnvDTE80;

using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VSTalk.Indicator;
using Microsoft.VSTalk.Integration;
using Microsoft.VSTalk.Integration.Events;
using Microsoft.VSTalk.Integration.Indicator;

using VSTalk.Engine;
using VSTalk.Engine.Core.ChatSession;
using VSTalk.Engine.Core.ClientManager;
using VSTalk.Engine.Core.Context;
using VSTalk.Engine.Core.Data;
using VSTalk.Engine.Core.Data.Migration;
using VSTalk.Engine.Core.Disco;
using VSTalk.Engine.Core.EntitySession;
using VSTalk.Engine.Core.EnvironmentManagers;
using VSTalk.Engine.Core.EnvironmentManagers.UI;
using VSTalk.Engine.Core.EnvironmentManagers.UI.Builders;
using VSTalk.Engine.Core.Event;
using VSTalk.Engine.Core.Event.Events;
using VSTalk.Engine.Core.ExtElement;
using VSTalk.Engine.Core.Feed;
using VSTalk.Engine.Core.Feed.MessageState;
using VSTalk.Engine.Core.History;
using VSTalk.Engine.Core.Notifications;
using VSTalk.Engine.Core.XmppConnection;
using VSTalk.Logger;

using log4net;

using VSTalk.Engine.Core.ContactInfo;
using VSTalk.Tools;


namespace Microsoft.VSTalk
{
    /// <summary>
    /// This is the class that implements the package exposed by this assembly.
    ///
    /// The minimum requirement for a class to be considered a valid package for Visual Studio
    /// is to implement the IVsPackage interface and register itself with the shell.
    /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
    /// to do it: it derives from the Package class that provides the implementation of the 
    /// IVsPackage interface and uses the registration attributes defined in the framework to 
    /// register itself and its components with the shell.
    /// </summary>
    // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is
    // a package.
    [PackageRegistration(UseManagedResourcesOnly = true)]
    // This attribute is used to register the informations needed to show the this package
    // in the Help/About dialog of Visual Studio.
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    // This attribute is needed to let the shell know that this package exposes some menus.
    [ProvideMenuResource("Menus.ctmenu", 1)]
    // This attribute registers a tool window exposed by this package.
    [ProvideToolWindow(typeof(BaseToolWindow))]
    [Guid(GuidList.guidVSTalkPkgString)]
    public sealed class VSTalkPackage : Package
    {
        private static readonly ILog _log = LoggerFactory.Create(typeof(VSTalkPackage));
        private DTE2 _ide;
        private ThemeManager _themeManager;

        /// <summary>
        /// Gets or sets the shell event listener.
        /// </summary>
        private ShellEventListener ShellEventListener { get; set; }

        /// <summary>
        /// Gets the shell service.
        /// </summary>
        private IVsShell ShellService
        {
            get { return GetService(typeof(SVsShell)) as IVsShell; }
        }

        /// <summary>
        /// Gets the top level application instance of the VS IDE that is executing this package.
        /// </summary>
        public DTE2 IDE
        {
            get { return _ide ?? (_ide = (DTE2) GetService(typeof(DTE))); }
        }

        /// <summary>
        /// Gets the theme manager.
        /// </summary>
        public ThemeManager ThemeManager
        {
            get { return _themeManager ?? (_themeManager = ThemeManager.GetInstance(this)); }
        }

        /// <summary>
        /// Default constructor of the package.
        /// Inside this method you can place any initialization code that does not require 
        /// any Visual Studio service because at this point the package object is created but 
        /// not sited yet inside Visual Studio environment. The place to do all the other 
        /// initialization is the Initialize method.
        /// </summary>
        public VSTalkPackage()
        {
            Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));
        }

        /// <summary>
        /// This function is called when the user clicks the menu item that shows the 
        /// tool window. See the Initialize method to see how the menu item is associated to 
        /// this function using the OleMenuCommandService service and the MenuCommand class.
        /// </summary>
        private void ShowToolWindow(object sender, EventArgs e)
        {
            // Get the instance number 0 of this tool window. This window is single instance so this instance
            // is actually the only one.
            // The last flag is set to true so that if the tool window does not exists it will be created.
            AppServices.Get<IWindowManager>().ShowMainWindow();
            //GetInterlocutorsAvatars();
        }

        



        /////////////////////////////////////////////////////////////////////////////
        // Overriden Package Implementation
        #region Package Members

        /// <summary>
        /// Initialization of the package; this method is called right after the package is sited, so this is the place
        /// where you can put all the initilaization code that rely on services provided by VisualStudio.
        /// </summary>
        protected override void Initialize()
        {
            Trace.WriteLine (string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));
            AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
                    _log.Error("Unhandled exception occured", args.ExceptionObject as Exception);

            _log.Info("Start initializing vstalk extension");
            base.Initialize();
            InitApp();
            // Add our command handlers for menu (commands must exist in the .vsct file)
            RegisterCommands();
            RegisterShellEventListener();
            _log.Info("vstalk extension initialized");
        }

        private void RegisterCommands()
        {
            var mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if (null == mcs) return;
            // GetOrCreate the command for the menu item.
            var menuCommandID = new CommandID(GuidList.guidVSTalkCmdSet, (int) PkgCmdIDList.cmdidVSTalk);
            var menuItem = new MenuCommand(ShowToolWindow, menuCommandID);
            mcs.AddCommand(menuItem);
            // GetOrCreate the command for the tool window
            var toolwndCommandID = new CommandID(GuidList.guidVSTalkCmdSet, (int) PkgCmdIDList.cmdidBaseToolWindow);
            var menuToolWin = new MenuCommand(ShowToolWindow, toolwndCommandID);
            mcs.AddCommand(menuToolWin);
        }

        private void RegisterShellEventListener()
        {
            ShellEventListener = new ShellEventListener(this, ShellService);
            ShellEventListener.EnvironmentColorChanged += () => ThemeManager.ApplyTheme();
        }

        private void InitApp()
        {
            try
            {
                _log.Info("Filling AppServices");

                var eventBus = new EventAggregator();
                AppServices.Set<IDatabaseManager>(InitializeDBManager());
                AppServices.Set<IEventAggregator>(eventBus);
                AppServices.Set<IEventFlow>(InitializeEventFlow());
                AppServices.Set<IEnvDataManager>(InitializeEnvironmentDataManager());
                AppServices.Set<IModelContext>(InitializeModelContext());
                AppServices.Set<INotificationQueue>(new NotificationQueue());
                AppServices.Set<IExtElementFactory>(new ExtElementFactory());
                AppServices.Set<IConnectionManager>(new ConnectionManager());
                AppServices.Set<ISessionModel>(InitializeSessionModelContext());
                AppServices.Set<IMessageFeed>(new MessageFeed());
                AppServices.Set<IMessageSateManager>(new MessageStateManager());
                AppServices.Set<IChatSessionFactory>(new ChatSessionFactory());
                AppServices.Set<IClientManager>(new ClientManager());
                AppServices.Set<IWindowManager>(InitializeWndManager());
                AppServices.Set<IHistoryService>(InitializeHistoryService());
                AppServices.Set<IDiscoService>(new DiscoService());
                AppServices.Set<IIndicatorManager>(InitializeIndicatorManager());
                AppServices.Set<IContactInfoSevice>(new ContactInfoSevice());
                
                _log.Info("AppServices filled, go go go... :)");
                eventBus.Publish(new AppInitializedEvent());
            }
            catch (Exception ex)
            {
                _log.Fatal("Unexpected exception", ex);
                throw;
            }
        }

        private static HistoryService InitializeHistoryService()
        {
            var dataProvider = new HistoryDataProvider();
            return new HistoryService(dataProvider);
        }

        private IDatabaseManager InitializeDBManager()
        {
            var migrationService = new MigrationService(new List<IMigrationScript>
            {
                new InitialMS(),
                new Migration1(),
                new Migration2()
            });
            var connectionFactory = new ConnectionFactory(migrationService);
            var databaseManager = new DatabaseManager(connectionFactory);
            return databaseManager;
        }

        private object InitializeWndManager()
        {
            return new WindowManager(
                    new EnvWindowsManager(this),
                    new HostedControls(new IControlBuilder[]
                    {
                        new MainControlBuilder(),
                        new ChatControlBuilder()
                    }),
                    new ResourceStore(),
                    ThemeManager);
        }

        private IEventFlow InitializeEventFlow()
        {
            var factory = new EventFlowFactory();
            return factory.Create();
        }

        private IIndicatorManager InitializeIndicatorManager()
        {
            var manager = new IndicatorManager();
            manager.Initialize();
            return manager;
        }

        private IEnvDataManager InitializeEnvironmentDataManager()
        {
            return new EnvDataManager();
        }

        private IEnvWindowsManager InitializeEnvironmentWindowManager()
        {
            return new EnvWindowsManager(this);
        }

        private IModelContext InitializeModelContext()
        {
            var initializer = new ModelInitializer();
            var context = initializer.InitializeContext();
            return context;
        }

        private ISessionModel InitializeSessionModelContext()
        {
            var initializer = new SessionModelFactory();
            var context = initializer.Create(AppServices.Get<IModelContext>());
            return context;
        }

        #endregion

        protected override void Dispose(bool disposing)
        {
            AppServices.Dispose();
            base.Dispose(disposing);
        }
    }
}
